home *** CD-ROM | disk | FTP | other *** search
- /*--------------------------------------------------------------------------*/
- /* The Opus Computer-Based Conversation System */
- /* (c) Copyright 1986, Wynn Wagner III, All Rights Reserved */
- /* */
- /* The original sealink protocol is copyrighted by SEA, inc. */
- /* */
- /* This module was written by W.Wagner III */
- /* with modifications done by Bob Hartman and Rick Huebner */
- /* */
- /* */
- /* GENERAL PURPOSE FILE TRANSMITTER (Xmodem/YModem/Sealink) */
- /* */
- /* */
- /* */
- /* This module is similar to a routine used by Opus-Cbcs (1.00). It is */
- /* provided for your information only. You will find routines that need */
- /* to be coded and identifiers to be resolved. */
- /* */
- /* There is absolutely no guarantee that anything here will work. If you */
- /* break this routine, you own both pieces. */
- /* */
- /* USAGE: You may use this material in any program with no obligation */
- /* as long as there is no charge for your program. For more */
- /* information about commercial use, contact the "OPUSinfo HERE" */
- /* BBS (124/111). */
- /* */
- /* */
- /*--------------------------------------------------------------------------*/
- #include <sys\types.h> /*ww/870225*/
- #include <sys\stat.h> /*ww/870225*/
- #include <sys\timeb.h>
- #include <errno.h>
- #include <stdio.h>
- #include "xfer.h"
- #include "com.h"
-
- extern byte *local_CEOL;
- extern int remote_capabilities;
- extern int daylight;
- extern long timezone;
- extern char *tzname[];
- extern int un_attended;
- extern int fullscreen;
-
- int small_window = 0;
- int no_overdrive = 0;
-
- static int send_ackless;
- static char *NOACKS_msg = "SEAlink Overdrive Disengaged";
-
- char *ultoa();
-
-
-
- /*--------------------------------------------------------------------------*/
- /* MULTI-PURPOSE FILE TRANSFER */
- /*--------------------------------------------------------------------------*/
- int send_file( fname, protocol )
- char *fname, protocol;
- begin
- register int i;
- register byte *b;
-
- struct FILEINFO dta;
- struct zero_block *header;
- int in_char;
- int ackblock;
- int blknum;
- unsigned int last_block;
- unsigned int errs;
- int sliding;
- int base;
- int head;
- int block_size;
- char *buffer;
- char *malloc();
- int do_chksum;
- FILE *fp;
- char *message;
- long block_timer, timerset();
- int win_size;
- int full_window;
- int may_be_seadog;
- int ackerr;
- struct stat st_stat;
-
- if (protocol == 'F')
- {
- protocol = 'S';
- may_be_seadog = 1;
- }
- else
- may_be_seadog = 0;
- fp = NULL;
- sliding = 0;
- ackblock = -1;
- do_chksum = errs = 0;
- ackerr = 0;
-
-
- /*--------------------------------------------------------------------*/
- /* The following window size works out to be between 3.5 and 4.5 */
- /* seconds of data at any valid baud rate. This gives us a set time */
- /* that the other end will wait before NAK'ing a block if it waits */
- /* to flush its buffer before NAK'ing. If it does not wait, then it */
- /* does not matter very much except to make sure we only have a set */
- /* amount of run-ahead that will fit in a 4K buffer */
- /*--------------------------------------------------------------------*/
- full_window = cur_baud/400;
-
- if (small_window && full_window > 6)
- full_window = 6;
-
- XON_DISABLE();
-
- /*--------------------------------------------------------------------*/
- /* Prepare file */
- /*--------------------------------------------------------------------*/
- if ((!fname) || (!fname[0])) /* No file? Just send an EOT. */
- begin
-
- CLEAR_INBOUND();
-
- for(i=0; i<5; i++)
- begin
- switch( in_char=TIMED_READ(5) )
- begin
-
- case 'C' :
- case NAK :
- case CAN : SENDBYTE(EOT);
- break;
-
- case TSYNC : return TSYNC;
-
- default : if (in_char<' ') return 0;
-
- end /* switch */
- end
- return 0;
- end
-
- strlwr(fname); /*ww/870225*/
- CLEAR_IOERR();
- if (!dfind(&dta, fname,0)) fp = fopen(fname,read_binary);
- else errno = ENOENT;
-
- if (got_error(OPEN_msg,fname))
- begin
- send_can();
- return 0;
- end
-
-
- /*--------------------------------------------------------------------*/
- /* Prepare buffer */
- /*--------------------------------------------------------------------*/
- buffer = malloc(1030);
- header = (struct zero_block *)buffer;
- if (!buffer)
- begin
- message = MEMOVFL_msg;
- goto fubar;
- end
-
-
- /*--------------------------------------------------------------------*/
- /* Prepare method */
- /*--------------------------------------------------------------------*/
- block_size = 128;
- head = SOH;
- switch(protocol)
- begin
- case 'Y' : base=1; block_size=1024; head=STX; break;
- case 'X' : base=1; break;
- case 'S' : base=0; break;
- case 'T' : base=0; head=SYN; break;
- case 'M' : base=1; break;
- default : status_line("!Protocol?"); return 0;
- end
- blknum= base;
-
- last_block = (int )((dta.size+((long )block_size-1L))/(long)block_size);
-
- if (un_attended && fullscreen)
- {
- clear_filetransfer();
- gotoxy (0,12);
- }
- cprintf("\r\nSend %s (%d %c-blks)",fname,last_block,protocol);
- set_xy( "Sending " );
-
-
- /*--------------------------------------------------------------------*/
- /* Wait for signal to begin */
- /*--------------------------------------------------------------------*/
- do
- begin
- i = TIMED_READ(5);
- switch ( i )
- begin
- case NAK : /*--------------------------------------------*/
- /* NAK */
- /*--------------------------------------------*/
- do_chksum = 1;
- /* fallthrough */
-
- case 'C' : /*--------------------------------------------*/
- /* CRC RESPONSE */
- /*--------------------------------------------*/
- begin
- int send_tmp;
- int send_tmp1;
- if (((send_tmp = TIMED_READ(1))>=0)
- &&((send_tmp1=TIMED_READ(1))==((~send_tmp)&0xff)))
- begin
- if (send_tmp <= 1)
- sliding=1;
- else
- begin
- /* Not a normal SEAlink startup */
- /* Lets end the transmission and */
- /* try again. Nasty, but it helps */
- SENDBYTE (EOT);
- continue;
- end
- end
- if (may_be_seadog)
- {
- sliding = 1;
- }
- errs = 0;
- CLEAR_INBOUND();
- throughput(0,0L);
- goto sendloop;
- end
-
- case CAN : /*--------------------------------------------*/
- /* CANCEL */
- /*--------------------------------------------*/
- message = CAN_msg;
- goto fubar;
-
- default : /*--------------------------------------------*/
- /* OTHER RESPONSES (debris) */
- /*--------------------------------------------*/
- if (((KEYPRESS()) and (READKB()==27)))
- begin
- message = KBD_msg;
- goto fubar;
- end
-
- else if ( (errs++)>15 )
- begin
- message = TIME_msg;
- goto fubar;
- end
- block_timer = timerset (50);
- while (!timeup (block_timer))
- time_release();
- CLEAR_INBOUND();
-
- end /* switch */
-
- end /* wait for signal to begin */
- while(CARRIER);
-
- message = CARRIER_msg;
- goto fubar;
-
-
- /*--------------------------------------------------------------------*/
- /* Transfer */
- /*--------------------------------------------------------------------*/
- sendloop:
- while(CARRIER)
- begin
-
- message = NULL;
- win_size = (blknum<2) ? 2 : (send_ackless ? 220 : full_window);
-
- if (((KEYPRESS()) and (READKB()==27)))
- begin
- message = KBD_msg;
- goto fubar;
- end
-
- if (blknum <= last_block)
- begin
- /*--------------------------------------------------------*/
- /* DATA */
- /*--------------------------------------------------------*/
- memset( buffer, 0, block_size );
- if (blknum)
- begin
- errno = 0;
- fseek( fp, ((long)(blknum-1)*(long)block_size), 0);
- if (got_error(READ_msg,fname))
- begin
- message = SEEK_msg;
- goto fubar;
- end
-
- errno = 0;
- fread( buffer, block_size, 1, fp );
- if (got_error(READ_msg,fname))
- begin
- message = READ_msg;
- goto fubar;
- end
- end
- else
- begin
- block_size = 128;
- header->size = dta.size;
- stat (fname, &st_stat);
- tzset();
- header->time = st_stat.st_atime-timezone;
-
- stcgfn( header->name, dta.name );
-
- if (protocol=='T')
- begin
- for(i=0; i<HEADER_NAMESIZE; i++)
- if (!(header->name[i])) header->name[i] = ' ';
- header->time = dta.time;
- end
-
- strcpy( header->moi, xfer_id );
- if ((cur_baud >= 9600) && (sliding))
- {
- header->noacks = 1;
- send_ackless = 1;
- }
- else
- {
- header->noacks = 0;
- send_ackless = 0;
- }
-
- if (no_overdrive)
- {
- header->noacks = 0;
- send_ackless = 0;
- }
- end
-
- /*--------------------------------------------------------*/
- /* HEAD */
- /*--------------------------------------------------------*/
- SENDBYTE( head );
- SENDBYTE( blknum );
- SENDBYTE( ~blknum );
-
-
- /*--------------------------------------------------------*/
- /* SEND */
- /*--------------------------------------------------------*/
- b = buffer;
- SENDCHARS (b, block_size, 1);
-
-
- /*--------------------------------------------------------*/
- /* CHECK */
- /*--------------------------------------------------------*/
- if ((do_chksum)||(head==SYN))
- begin
- unsigned char chksum = '\0';
- for ( b=buffer,i=0; i<block_size; i++, b++ )
- chksum+=(*b);
- SENDBYTE( chksum );
- end
- else
- begin
- word crc;
-
- for(b=buffer, crc=i=0; i<block_size; i++, b++)
- crc = xcrc(crc,(byte )(*b));
-
- SENDBYTE( crc>>8 );
- SENDBYTE( crc&0xff );
- end
- end /* if not finished */
-
- /*--------------------------------------------------------------*/
- /* Only wait 30 seconds for a response */
- /* For non-sliding this number is updated, for sliding we have */
- /* a problem since the output buffer may still have stuff in */
- /* it. We need to make sure we reset this when output is done */
- /* in the sliding case. What we actually do is reset it if the */
- /* output has not yet completed. We only check it in the case */
- /* of debris, then recycle, so this should be a reasonable way */
- /* of doing things in the sliding case. */
- /*--------------------------------------------------------------*/
- block_timer = timerset(3000);
-
- slide_reply:
- /*--------------------------------------------------------------*/
- /* If we are not sliding, force the output and purge the input. */
- /* This is simply good Xmodem practice. It is also necessary */
- /* to make sure that the other end has received all of the data */
- /* before starting the block reply timeout. */
- /*--------------------------------------------------------------*/
- if (!sliding)
- begin
- /*
- CLEAR_INBOUND();
- */
- while (!OUT_EMPTY())
- time_release();
- block_timer = timerset(3000);
- end
-
- /*--------------------------------------------------------------*/
- /* REPLY: Slide */
- /* Don't wait, unless other end has something to say */
- /* or we're at/beyond the window. */
- /*--------------------------------------------------------------*/
- else if ( (blknum < (ackblock+win_size)) and /* in window */
- (blknum < last_block ) and /* have more to send */
- (PEEKBYTE()< 0 ) /* other end is quiet */
- )
- begin
- if ((send_ackless) && (blknum > 0))
- {
- ackblock = blknum;
-
- if (++blknum > last_block)
- {
- goto done;
- }
-
- if ((blknum % 20) == 0)
- {
- gotoxy(locate_x,locate_y);
- cputs( ultoa(((unsigned long )(blknum)),e_input,10) );
- bdos (9,"*$");
- }
- }
- else
- {
- blknum++;
- }
- goto sendloop;
- end
-
- if (PEEKBYTE() < 0)
- {
- if (send_ackless)
- {
- ackblock = blknum;
-
- if (++blknum > last_block)
- {
- goto done;
- }
-
- if ((blknum % 20) == 0)
- {
- gotoxy(locate_x,locate_y);
- cputs( ultoa(((unsigned long )(blknum)),e_input,10) );
- bdos (9,"*$");
- }
-
- goto sendloop;
- }
- }
-
-
- /*--------------------------------------------------------------*/
- reply: /* REPLY: Wait state */
- /* No sliding, or slid too far. Wait for a reply. */
- /* */
- /* If we got here we are either not sliding, or we are sliding */
- /* and have received an input character, or we are sliding and */
- /* have slid too far. In the first case the following loop */
- /* will not do anything because the output is empty, in the */
- /* second case we also will not do anything since we have an */
- /* input character ready, in the final case we want to execute */
- /* the loop since we are in a wait state. The loop will go on */
- /* until the output is empty (in the worst case), and since we */
- /* we have disabled flow control here (right???) we will never */
- /* have to worry about the loop not terminating. Now, we just */
- /* sit and loop as long as there is output to do and no input. */
- /* Just to make wwIII happy do a time_release() so that his */
- /* DDos system will be more efficient! */
- /*--------------------------------------------------------------*/
-
- while ( (!OUT_EMPTY()) and
- (PEEKBYTE()<0))
- time_release(); /* ... time slice routine for multi-taskers */
-
- /*--------------------------------------------------------------*/
- /* If we got here we either sent all the output, or got input. */
- /* In the first case we should get a response within 15 seconds */
- /* In the second case we should get an immediate response. */
- /* Either way, nothing in 30 seconds is a timeout */
- /*--------------------------------------------------------------*/
- if ((in_char=TIMED_READ(30))<0)
- begin
- message = TIME_msg;
- goto fubar;
- end
-
- if (in_char=='C')
- begin
- do_chksum = 0;
- in_char = NAK;
- end
-
-
- /*--------------------------------------------------------------*/
- /* CAN */
- /*--------------------------------------------------------------*/
- if (in_char==CAN)
- begin
- message = CAN_msg;
- goto fubar;
- end
-
-
- /*--------------------------------------------------------------*/
- /* REPLY: Take action based on slide-response */
- /*--------------------------------------------------------------*/
- if ((in_char>0) and (sliding))
- begin
-
- register int i, j;
-
- if (++ackerr >= 10)
- {
- if (send_ackless)
- {
- send_ackless = 0;
- message = NOACKS_msg;
- }
- }
-
- if ((in_char==ACK) || (in_char==NAK))
- begin
- if ((i=TIMED_READ(2))<0)
- begin
- sliding=0;
- if (send_ackless)
- {
- send_ackless = 0;
- message = NOACKS_msg;
- }
- end
- else
- begin
- if ((j=TIMED_READ(2))<0)
- begin
- sliding = 0;
- if (send_ackless)
- {
- send_ackless = 0;
- message = NOACKS_msg;
- }
- end
- else if (i==(j^0xff))
- begin
- i = blknum-((blknum-i)&0xff);
- if ((i<=blknum)&&(i>(blknum-win_size-10)))
- begin
- if (in_char==ACK)
- begin
- ackblock=i;
- blknum++;
- if (ackblock>=last_block)
- begin
- goto done;
- end
- errs = 0;
-
- if ((head==SYN)&&(blknum))
- head = SOH; /* ZapTeLink */
-
- end
- else
- begin
- blknum=i;
- /*CLEAR_INBOUND();*/
- CLEAR_OUTBOUND();
- errs++;
- end
- end
- end
- else
- begin
- message = "SLIDE CMPL ERR";
- end
- end
- end
- else
- begin
-
- /*--------------------------------------------------*/
- /* If we got debris, just ignore it and go on if we */
- /* have not yet had a timeout. If we timed out, */
- /* then just go to the fubar label and abort */
- /*--------------------------------------------------*/
-
- gotoxy(locate_x+10,locate_y);
- cprintf(" Debris [%Xh]",in_char);
-
-
- /*--------------------------------------------------*/
- /* The error is timeout with the output buffer */
- /* empty (we can't penalize someone for a timeout */
- /* if we haven't yet sent them all they need!!! */
- /*--------------------------------------------------*/
-
- if (timeup(block_timer) and OUT_EMPTY())
- begin
- message = TIME_msg;
- goto fubar;
- end
-
- /*--------------------------------------------------*/
- /* Ok, we either have a timeout or the buffer still */
- /* has stuff in it. If it still has stuff, reset */
- /* the timeout variable before trying again */
- /*--------------------------------------------------*/
-
- else if (!OUT_EMPTY()) block_timer = timerset(3000);
-
- goto slide_reply;
- end
- end
-
- /*--------------------------------------------------------------*/
- /* REPLY: Take action based on regular response */
- /*--------------------------------------------------------------*/
- if (!sliding)
- begin
- if (in_char==ACK)
- begin
-
- /*--------------------------------------------------*/
- /* On block 10, throw in an extra pause to give the */
- /* other end a change to fall into a slide if it */
- /* feels it's something it needs to do. */
- /*--------------------------------------------------*/
- if (blknum==10) timer(3); /* approx. 3/10s second */
-
- /*--------------------------------------------------*/
- /* Smell the other system vis-a-vis a slide. */
- /*--------------------------------------------------*/
- if (PEEKBYTE()>0)
- begin
- int send_tmp;
- if (((send_tmp = TIMED_READ(1))>=0)
- &&(TIMED_READ(1)==((~send_tmp)&0xff)))
- begin
- sliding = 1;
- ackblock = send_tmp;
- end
- end
- /*if (!sliding) CLEAR_INBOUND();*/
-
-
- message = NULL;
- if (blknum>=last_block) goto done;
- blknum++;
-
- if ((head==SYN)&&(blknum)) head = SOH; /* ZapTeLink */
-
- errs = 0;
- end
- else if (in_char==NAK)
- begin
- errs++;
- timer(5);
- /*CLEAR_INBOUND();*/
- CLEAR_OUTBOUND();
- message = NAK_msg;
- end
- else if (CARRIER)
- begin
- /* Did we time out yet? */
- if (!timeup(block_timer))
- goto reply;
- else
- begin
- message = TIME_msg;
- goto fubar;
- end
- end
- else
- begin
- message = CARRIER_msg;
- goto fubar;
- end
- end
-
- if (errs>10)
- begin
- message = FUBAR_msg;
- goto fubar;
- end
-
- bottom: gotoxy(locate_x,locate_y);
- i = (blknum<=last_block)?blknum:last_block;
- cputs( ultoa(((unsigned long )(i)),e_input,10) );
-
- if ((sliding) and (ackblock>0))
- begin
- if (send_ackless)
- bdos (9,"*$");
- else
- {
- bdos(9," : $");
- cputs( ultoa(((unsigned long )(ackblock)),e_input,10) );
- }
- end
-
- if (message)
- begin
- putch(' ');
- cputs( message );
- cputs( local_CEOL );
- end
-
- end /* while */
-
- message = CARRIER_msg;
-
-
-
- fubar:
-
- CLEAR_OUTBOUND();
- send_can();
-
- status_line("!%s not sent: %s",fname,message);
-
- if (fp) fclose(fp);
- if (buffer) free(buffer);
- /*CLEAR_INBOUND();*/
-
- return 0;
-
-
- done:
- while (!OUT_EMPTY())
- time_release();
-
- if (fp) fclose(fp);
- if (buffer) free(buffer);
-
- /* Here I am going to delay some to make sure everything is cool */
- if (cur_baud > 2400)
- {
- block_timer = timerset (200);
- while (CARRIER && !timeup (block_timer))
- time_release();
- }
-
- CLEAR_INBOUND();
- SENDBYTE (EOT);
-
- ackerr = 1;
-
- for(i=0; i<5; i++)
- begin
- if (!CARRIER)
- {
- ackerr = 1;
- goto gohome;
- }
-
- switch( TIMED_READ(5) )
- begin
-
- case 'C':
- case NAK:
- case CAN:
- if (sliding && ((in_char = TIMED_READ(1)) >= 0))
- {
- TIMED_READ(1);
- }
- CLEAR_INBOUND();
- SENDBYTE(EOT);
- break;
-
- case TSYNC: ackerr = TSYNC;
- goto gohome;
-
- case ACK:
- if (sliding && ((in_char = TIMED_READ(1)) >= 0))
- {
- TIMED_READ(1);
- }
- ackerr = 1;
- goto gohome;
-
- end /* switch */
- end
-
- gohome:
-
- if (locate_y>1) gotoxy(0,locate_y-1);
- throughput(1,dta.size);
-
- status_line( "+Sent-S %s", fname );
-
- return ackerr;
-
- end
-
-
- /* END OF FILE: F_Send.C */